package types

import (
	"encoding/hex"
	"fmt"
)

// HeadOffset head and offset of find result
type HeadOffset struct {
	Head   Header32
	Offset uint32
}

func (ho *HeadOffset) String() string {
	return fmt.Sprintf("%s O:%10d",
		ho.Head.String(),
		ho.Offset,
	)
}

// Selector find strategy
type Selector interface {
	Determine([]*HeadOffset) *HeadOffset
	String() string
	Bin() []byte
	Hex() string
}

type maxSelect struct{}

// HashSelect match hash and max size strategy
type HashSelect Hash

// FullSelect match hash,crc and size strategy
type FullSelect Hash256

func (maxSelect) String() string {
	return "temp"
}

func (maxSelect) Bin() []byte {
	return []byte{0, 0, 0, 0}
}

func (maxSelect) Hex() string {
	return "0000"
}

func (maxSelect) Determine(objs []*HeadOffset) (obj *HeadOffset) {
	if len(objs) == 0 {
		return nil
	}
	var size uint32
	for _, v := range objs {
		if v.Head.Size > size {
			obj = v
			size = v.Head.Size
		}
	}
	return
}

// Determine find out hash matched and max size
func (hash HashSelect) Determine(objs []*HeadOffset) *HeadOffset {
	if len(objs) == 0 {
		return nil
	}
	var maxidx = 0
	var find = false
	for k, v := range objs {
		if v.Head.Hash == Hash(hash) && v.Head.Size >= objs[maxidx].Head.Size {
			maxidx = k
			find = true
		}
	}
	if find {
		return objs[maxidx]
	}
	return nil
}

func (hash HashSelect) String() string {
	return Hash(hash).String()
}

// Bin inteface
func (hash HashSelect) Bin() []byte {
	return hash[:]
}

//Hex inferface
func (hash HashSelect) Hex() string {
	return Hash(hash).String()
}

// Determine find out hash256 matched
func (buf FullSelect) Determine(objs []*HeadOffset) *HeadOffset {
	for _, v := range objs {
		if v.Head.ToHash256() == Hash256(buf) {
			return v
		}
	}
	return nil
}

func (buf FullSelect) String() string {
	h := Hash256(buf)
	h32, err := h.ToHeader32()
	if err != nil {
		return h32.String() + " with " + err.Error()
	}
	return h32.String()
}

// Bin inteface
func (buf FullSelect) Bin() []byte {
	return buf[:]
}

//Hex inferface
func (buf FullSelect) Hex() string {
	return hex.EncodeToString(buf[:])
}

// MaxSelect max strategy
var MaxSelect maxSelect

// Selector2Hash convert selector to hash
func Selector2Hash(s Selector) (vs Hash, err error) {
	switch hs := s.(type) {
	case HashSelect:
		vs = Hash(hs)
	case FullSelect:
		var o *Header32
		var h256 = Hash256(hs)
		o, err = h256.ToHeader32()
		if err != nil {
			return
		}
		vs = o.Hash
	default:
		err = fmt.Errorf("unsupport selector to hash")
	}
	return
}
